/**
 * 系统编译环境管理路由
 *
 * Created by snowingsea on 2020/11/02.
 */
const path = require('path');
const fs = require('fs-extra');
const compressing = require('compressing');
const randomstring = require('randomstring');
const middleware = require('./../middleware');
const environments = require('./../../models/environments');
const files = require('./../../models/files');
const gitClient = require('./../../models/git/git-client');
const PhoenixError = require('./../../models/phoenix-error');
const utility = require('./../../models/utility');
const cacheUtil = require('./../../cacheUtil');
const uploadProgress = require('../middleware/upload-progress');

/**
 * 从副本复制环境
 * @param req
 * @param res
 * @param next
 */
function addEnvironmentByCopy(req, res, next) {
  if (!req.query.environmentName) {
    next(new PhoenixError('invalid request', 'Missing parameters. "environmentName" is required'));
    return;
  }

  const {
    $systemEnvironmentProject,
    $superUser,
    query,
  } = req;
  let environment;
  let copy;
  const projectId = req.$systemEnvironmentProject._id; // eslint-disable-line no-underscore-dangle
  environments.getEnvironment(req.query.copyId)
    .then((res) => {
      copy = res;
      if (copy) {
        req.query.description = req.query.description || copy.description;
        req.query.includes = req.query.includes || copy.includes;
        req.query.macros = req.query.macros || copy.macros;
        req.query.arguments = req.query.arguments || copy.arguments;
      }
      return environments.addEnvironment(projectId, query, $systemEnvironmentProject, $superUser);
    })
    .then((res) => {
      environment = res;
      if (copy) {
        const sourcecodeDir = cacheUtil.getConfigPath('sourcecodeDir');
        const zipPath = path.join(sourcecodeDir, `${randomstring.generate(16)}.zip`);
        let exportPath;
        return gitClient.archive($superUser, $systemEnvironmentProject, copy)
          .then((path) => {
            exportPath = path;
            return fs.ensureDir(sourcecodeDir);
          })
          .then(() => compressing.zip.compressDir(exportPath, zipPath))
          .then(() => fs.rmdir(exportPath, { recursive: true }))
          .then(() => files.updateByUploadFile(
            environment.rootDirectory._id, // eslint-disable-line no-underscore-dangle
            zipPath,
            environment.rootDirectory,
            environment,
            $systemEnvironmentProject,
            $superUser,
          ));
      }
      return Promise.resolve();
    })
    .then(() => {
      res.json(environments.getJson(environment));
    })
    .catch((err) => {
      if (err.code === 11000) {
        err = new PhoenixError('conflict', 'environment already exist');
      }
      next(new PhoenixError(err));
    });
}

/**
 * 系统管理员添加系统编译环境
 * @param req
 * @param res
 * @param next
 */
function addEnvironment(req, res, next) {
  if (req.query.copyId) {
    addEnvironmentByCopy(req, res, next);
    return;
  }

  if (!req.query.environmentName) {
    next(new PhoenixError('invalid request', 'Missing parameters. "environmentName" is required'));
    return;
  }

  const projectId = req.$systemEnvironmentProject._id; // eslint-disable-line no-underscore-dangle
  environments.addEnvironment(projectId, req.query, req.$systemEnvironmentProject, req.$superUser)
    .then((environment) => {
      res.json(environments.getJson(environment));
    })
    .catch((err) => {
      if (err.code === 11000) {
        err = new PhoenixError('conflict', 'environment already exist');
      }
      next(new PhoenixError(err));
    });
}

/**
 * 删除系统编译环境
 * @param req
 * @param res
 * @param next
 */
function removeEnvironment(req, res, next) {
  const { environmentId } = req.params;
  const { $environment, $systemEnvironmentProject, $superUser } = req;
  environments.removeEnvironment(environmentId, $environment, $systemEnvironmentProject, $superUser)
    .then(() => {
      res.status(204).json();
    })
    .catch((err) => {
      if (err.message.indexOf('fatal: Unable to create') !== -1 && err.message.indexOf('index.lock') !== -1) {
        err = new PhoenixError('git index lock', 'Environment deletion is busy');
      }
      next(new PhoenixError(err));
    });
}

/**
 * 批量删除系统编译环境
 * @param req
 * @param res
 * @param next
 */
function removeEnvironments(req, res, next) {
  const options = environments.getEnvironmentOptions(req.query);
  options.projectId = req.$systemEnvironmentProject._id; // eslint-disable-line no-underscore-dangle
  environments.getEnvironments(req.query, options)
    .then((resEnvironments) => {
      const { $systemEnvironmentProject, $superUser } = req;
      function step(index) {
        if (index >= resEnvironments.length) {
          res.status(204).json();
          return;
        }

        const env = resEnvironments[index];
        const envId = env._id; // eslint-disable-line no-underscore-dangle
        environments.removeEnvironment(envId, env, $systemEnvironmentProject, $superUser)
          .then(() => {
            step(index + 1);
          })
          .catch((err) => {
            next(new PhoenixError(err));
          });
      }
      step(0);
    })
    .catch((err) => {
      if (err.message.indexOf('fatal: Unable to create') !== -1 && err.message.indexOf('index.lock') !== -1) {
        err = new PhoenixError('git index lock', 'Environment deletion is busy');
      }
      next(new PhoenixError(err));
    });
}

/**
 * 编辑系统编译环境信息
 * @param req
 * @param res
 * @param next
 */
function updateEnvironment(req, res, next) {
  const { environmentId } = req.params;
  const { $environment, $superUser } = req;
  const project = req.$systemEnvironmentProject;
  environments.updateEnvironment(environmentId, req.query, $environment, project, $superUser)
    .then(() => environments.getEnvironment(environmentId))
    .then((environment) => {
      res.json(environments.getJson(environment));
    })
    .catch((err) => {
      if (err.code === 11000) {
        err = new PhoenixError('conflict', 'Environment already exist');
      }
      next(new PhoenixError(err));
    });
}

/**
 * 导出编译环境信息
 * @param  req
 * @param  res
 * @param  next
 */
function exportEnvironment(req, res, next) {
  (async () => {
    const sourcecodeDir = cacheUtil.getConfigPath('sourcecodeDir');
    if (!(await utility.doesFileExist(sourcecodeDir))) {
      await fs.mkdir(sourcecodeDir);
    }
    const {
      versionName,
      includes,
      arguments: args,
      macros,
    } = req.$environment;
    const zipPath = path.join(sourcecodeDir, `${randomstring.generate(16)}.zip`);
    const exportPath = await gitClient.archive(req.$superUser, req.$systemEnvironmentProject, req.$environment);
    // 排除垃圾文件
    await Promise.all(['.gitignore', '.smartrocket', '__MACOSX'].map((file) => fs.remove(path.join(exportPath, file))));
    // 写入配置文件
    const configFilePath = path.join(exportPath, 'environment_config.json');
    await fs.writeFile(configFilePath, JSON.stringify({ includes, arguments: args, macros: macros.map((m) => ({ key: m.key, value: m.value })) }));
    await compressing.zip.compressDir(exportPath, zipPath, { ignoreBase: true });
    await fs.rmdir(exportPath, { recursive: true });
    res.setHeader('Access-Control-Expose-Headers', 'Content-Disposition');
    res.download(zipPath, `${versionName}.zip`, () => fs.unlink(zipPath));
  })().catch((err) => {
    next(new PhoenixError(err));
  });
}

/**
 * 导入编译环境信息
 * @param  req
 * @param  res
 * @param  next
 */
function importEnvironment(req, res, next) {
  (async () => {
    if (!req.body.environmentName) {
      next(new PhoenixError('invalid request', 'Missing parameters. "environmentName" is required'));
      return;
    }
    if (!req.file) {
      next(new PhoenixError('invalid request', 'Missing parameters. "file" is required'));
      return;
    }
    const newEnvironment = await environments.addEnvironment(
      req.$systemEnvironmentProject._id,
      {
        ...req.query,
        ...req.body,
      },
      req.$systemEnvironmentProject,
      req.$superUser
    );
    const reuslt = await files.importEnvironmentFiles(
      req.file.path,
      req.$superUser,
      req.$systemEnvironmentProject,
      newEnvironment,
      req.body.zipFileNameEncoding || 'gb2312'
    );
    await environments.updateSmartRocketFile(newEnvironment._id);
    res.json(reuslt);
  })().catch((err) => {
    if (err.code === 11000) {
      err = new PhoenixError('conflict', 'Environment already exist');
    }
    next(new PhoenixError(err));
  });
}
/**
 * Export model definition object.
 */
module.exports = (app, prefix) => {
  const environmentsURL = `${prefix}/environments.json`;
  const environmentURL = `${prefix}/environments/:environmentId.json`;
  const exportURL = `${prefix}/environments/export/:environmentId.json`;
  const importURL = `${prefix}/environments/import`;
  const upload = uploadProgress;

  // 获取系统编译环境信息
  app.get(environmentURL, middleware.user.requireTokenUnexpired);
  app.get(environmentURL, middleware.user.requireAvailable);
  app.get(environmentURL, middleware.user.requireAdministrator);
  app.get(environmentURL, middleware.environment.requireEnvironmentExist);
  app.get(environmentURL, middleware.environment.getEnvironment);

  // 获取系统编译环境列表
  app.get(environmentsURL, middleware.user.requireTokenUnexpired);
  app.get(environmentsURL, middleware.user.requireAvailable);
  app.get(environmentsURL, middleware.user.requireAdministrator);
  app.get(environmentsURL, middleware.splitFilter);
  app.get(environmentsURL, middleware.user.getSuperUser);
  app.get(environmentsURL, middleware.environment.requireSystemEnvironmentProjectExist);
  app.get(environmentsURL, middleware.environment.getSystemEnvironments);

  // 添加系统编译环境
  app.post(environmentsURL, middleware.user.requireTokenUnexpired);
  app.post(environmentsURL, middleware.user.requireAvailable);
  app.post(environmentsURL, middleware.user.requireAdministrator);
  app.post(environmentsURL, middleware.user.getSuperUser);
  app.post(environmentsURL, middleware.environment.requireSystemEnvironmentProjectExist);
  app.post(environmentsURL, middleware.projectVersion.parseVersionQuery);
  app.post(environmentsURL, addEnvironment);

  // 删除系统编译环境
  app.delete(environmentURL, middleware.user.requireTokenUnexpired);
  app.delete(environmentURL, middleware.user.requireAvailable);
  app.delete(environmentURL, middleware.user.requireAdministrator);
  app.delete(environmentURL, middleware.environment.requireEnvironmentExist);
  app.delete(environmentURL, removeEnvironment);

  // 批量删除系统编译环境
  app.delete(environmentsURL, middleware.user.requireTokenUnexpired);
  app.delete(environmentsURL, middleware.user.requireAvailable);
  app.delete(environmentsURL, middleware.user.requireAdministrator);
  app.delete(environmentsURL, middleware.user.getSuperUser);
  app.delete(environmentsURL, middleware.environment.requireSystemEnvironmentProjectExist);
  app.delete(environmentsURL, removeEnvironments);

  // 编辑系统编译环境信息
  app.put(environmentURL, middleware.user.requireTokenUnexpired);
  app.put(environmentURL, middleware.user.requireAvailable);
  app.put(environmentURL, middleware.user.requireAdministrator);
  app.put(environmentURL, middleware.user.getSuperUser);
  app.put(environmentURL, middleware.environment.requireSystemEnvironmentProjectExist);
  app.put(environmentURL, middleware.environment.requireEnvironmentExist);
  app.put(environmentURL, middleware.projectVersion.parseVersionQuery);
  app.put(environmentURL, updateEnvironment);

  // 导出系统编译环境信息
  app.get(exportURL, middleware.user.requireTokenUnexpired);
  app.get(exportURL, middleware.user.requireAvailable);
  app.get(exportURL, middleware.user.requireAdministrator);
  app.get(exportURL, middleware.user.getSuperUser);
  app.get(exportURL, middleware.environment.requireSystemEnvironmentProjectExist);
  app.get(exportURL, middleware.environment.requireEnvironmentExist);
  app.get(exportURL, exportEnvironment);

  // 导入系统编译环境信息
  app.post(importURL, middleware.user.requireTokenUnexpired);
  app.post(importURL, middleware.user.requireAvailable);
  app.post(importURL, middleware.user.requireAdministrator);
  app.post(importURL, middleware.user.getSuperUser);
  app.post(importURL, middleware.environment.requireSystemEnvironmentProjectExist);
  app.post(importURL, middleware.projectVersion.parseVersionQuery);
  app.post(importURL, middleware.progressNotify());
  app.post(importURL, upload.single('file'));
  app.post(importURL, importEnvironment);
};
